// Script Manager
// (c) jimon game studio

#ifndef JEH_ISCRIPTMANAGER
#define JEH_ISCRIPTMANAGER

#include "jeUnknown.h"
#include "jeSingleton.h"
#include "IBuffer.h"
#include "jeStl.h"

namespace je
{
	namespace core
	{
		//! Script Object
		class IScriptObject:public je::jeUnknown
		{
		public:
			//! Destructor
			virtual ~IScriptObject()
			{
			}

			//! Return Pointer
			virtual void * GetPointer() = 0;
		};

		//! Script Manager
		class IScriptManager:public je::jeUnknown
		{
		protected:

			//! Script Function Name
			struct ScriptFunctionName
			{
				jeStringc Name;
				jeStringc Declaration;
			};

			typedef jeList<ScriptFunctionName> FunctionNamesListType;
			FunctionNamesListType FunctionNames;

		public:
			//! Script Function Type
			typedef void (*jeScriptFunction)();

			//! Script Method Type
			class jeScriptMethodPtrBase
			{
			public:
				virtual ~jeScriptMethodPtrBase(){}
				virtual void * GetPtrData() = 0;
				virtual s32 GetPtrSize() = 0;
			};

			template<class T>
			class jeScriptMethodPtr:public jeScriptMethodPtrBase
			{
			protected:
				T Ptr;
			public:
				jeScriptMethodPtr(T NewPtr)
				{
					Ptr = NewPtr;
				}
				~jeScriptMethodPtr()
				{
				}
				void * GetPtrData(){return reinterpret_cast<void*>(&Ptr);}
				s32 GetPtrSize(){return sizeof(T);}
				static jeScriptMethodPtrBase * Create(T NewPtr)
				{
					jeScriptMethodPtr<T> * Value = NULL;
					JENEW(Value,jeScriptMethodPtr<T>(NewPtr))
					return Value;
				}
			};

			//! Destructor
			virtual ~IScriptManager()
			{
				FunctionNames.clear();
			}

			//! Load Script
			virtual u1 LoadScript(core::IBuffer * Buffer) = 0;

			//! Load Byte Code
			virtual u1 LoadByteCode(core::IBuffer * Buffer) = 0;

			//! Save Byte Code
			virtual core::IBuffer * SaveByteCode() = 0;

			//! Register Function
			virtual void RegisterFunction(const jeStringc & Declaration,const jeScriptFunction & FunctionPtr) = 0;

			//! Register Class
			virtual void RegisterClass(const jeStringc & ClassName,s32 ClassSize) = 0;

			//! Register Class
			virtual void RegisterClassBase(const jeStringc & ClassName,const jeStringc & BaseClassName,s32 ClassSize) = 0;

			//! Register Class Method
			virtual void RegisterClassMethod(const jeStringc & ClassName,const jeStringc & MethodDefinition,jeScriptMethodPtrBase * MethodPtr) = 0;

			//! Register Class Method Variable
			virtual void RegisterClassVariable(const jeStringc & ClassName,const jeStringc & VariableDefinition,s32 VariableOffset) = 0;

			//! Register Global Variable
			virtual void RegisterGlobalVariable(const jeStringc & Declaration,void * Pointer) = 0;

			//! Execute Command
			virtual void ExecuteCommand(const jeStringc & Command) = 0;

			//! Gargabe Collector
			virtual void GarbageCollector() = 0;

			//! Add Function Fastcall
			virtual void AddFunctionFastcallDeclaration(const jeStringc & Name,const jeStringc & Declaration)
			{
				FunctionNames.push_back(ScriptFunctionName());
				FunctionNames.getLast()->Name = Name;
				FunctionNames.getLast()->Declaration = Declaration;
			}

			//! Add Function Param
			virtual void AddParam(u1 Param) = 0;

			//! Add Function Param
			virtual void AddParam(c8 Param) = 0;

			//! Add Function Param
			virtual void AddParam(s32 Param) = 0;

			//! Add Function Param
			virtual void AddParam(f32 Param) = 0;

			//! Add Function Param
			virtual void AddParam(void * Param) = 0;

			//! Call Function,return true if successful
			virtual u1 CallFunction(const jeStringc & FunctionName = "") = 0;

			//! Call Method,return true if successful
			virtual u1 CallMethod(const jeStringc & MethodName,IScriptObject * Object) = 0;

			//! Fast Call Function,return true if successful
			virtual u1 FastCallFunction(const jeStringc & FunctionName = "")
			{
				for(FunctionNamesListType::Iterator Iter = FunctionNames.begin();Iter != FunctionNames.end();Iter++)
					if((*Iter).Name == FunctionName)
						return CallFunction((*Iter).Declaration);
				return false;
			}

			//! Execute Call
			virtual u1 ExecuteCall() = 0;

			//! Get Result
			virtual s8 GetResults8() = 0;

			//! Get Result
			virtual s32 GetResults32() = 0;

			//! Get Result
			virtual f32 GetResultf32() = 0;

			//! Get Result
			virtual void * GetResultPointer() = 0;

			//! Create Script Object
			virtual IScriptObject * CreateScriptObject(const jeStringc & Name) = 0;
		};

		DECLARE_SINGLETON_E(JE_ENGINE_FUNCTION_API,JE_ENGINE_FUNCTION_CALL,ScriptManager)
	}
}

#define JE_SCRIPT_VAR_OFFSET(__Class,__Variable) (je_size_t)&reinterpret_cast<const volatile char&>(((__Class*)0)->__Variable)
#define JE_SCRIPT_FUNCTION(__Function) reinterpret_cast<je::core::IScriptManager::jeScriptFunction>(&__Function)
#define JE_SCRIPT_METHOD(__Class,__Method) je::core::IScriptManager::jeScriptMethodPtr<void (__Class::*)()>::Create(reinterpret_cast<void (__Class::*)()>(&__Class::__Method))
#define JE_SCRIPT_METHOD_PR(__Class,__Method,__Return,__Param) je::core::IScriptManager::jeScriptMethodPtr<void (__Class::*)()>::Create(reinterpret_cast<void (__Class::*)()>((__Return (__Class::*)__Param)(&__Class::__Method)))

#define JE_SCRIPT_REG_FUNCTION(__Function,__Declaration) je::core::GetScriptManager()->RegisterFunction(__Declaration,JE_SCRIPT_FUNCTION(__Function));
#define JE_SCRIPT_REG_CLASS(__Class) je::core::GetScriptManager()->RegisterClass(#__Class,sizeof(__Class));
#define JE_SCRIPT_REG_CLASS_BASE(__Class,__Class_Base) je::core::GetScriptManager()->RegisterClassBase(#__Class,#__Class_Base,sizeof(__Class));
#define JE_SCRIPT_REG_CLASS_METHOD(__Class,__Method,__Declaration) {je::core::IScriptManager::jeScriptMethodPtrBase * __Ptr__##__Class = JE_SCRIPT_METHOD(__Class,__Method); je::core::GetScriptManager()->RegisterClassMethod(#__Class,__Declaration,__Ptr__##__Class); JEDELETE(__Ptr__##__Class)}
#define JE_SCRIPT_REG_CLASS_METHOD_PR(__Class,__Return,__Method,__Param,__Declaration) {je::core::IScriptManager::jeScriptMethodPtrBase * __Ptr__##__Class = JE_SCRIPT_METHOD_PR(__Class,__Method,__Return,__Param); je::core::GetScriptManager()->RegisterClassMethod(#__Class,__Declaration,__Ptr__##__Class); JEDELETE(__Ptr__##__Class)}
#define JE_SCRIPT_REG_CLASS_VARIABLE(__Class,__Variable,__Declaration) je::core::GetScriptManager()->RegisterClassVariable(#__Class,__Declaration,JE_SCRIPT_VAR_OFFSET(__Class,__Variable));

#endif
